﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CloudAppMonitor.Properties;
using CloudAppMonitor.Data;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using CloudAppMonitor.Library;
using CloudAppMonitor.Data.EntityData;
using System.Xml.Linq;

namespace CloudAppMonitor
{
    public partial class Monitor : Form
    {
        public const string EVENT_SOURCE = "CloudAppMonitor";

        Settings _settings = new Settings();
        AzureDataDownload _azureDataDownload;
        SqlStorage _storage;
        DateTime? _oldestDate;
        DateTime? _newestDate;
        Guid _lastUpdateToken;
        IEnumerable<Counter> _currentCounters;
        IEnumerable<RoleInstance> _currentRoleInstances;

        public Monitor()
        {
            InitializeComponent();

            if (!string.IsNullOrEmpty(_settings.DatabaseName) && !string.IsNullOrEmpty(_settings.DatabaseServer))
            {
                _storage = new SqlStorage(_settings);
            }

            _azureDataDownload = new AzureDataDownload(_settings);

            LoadMonitoredPerfCounters();

            LoadPerfCounterValues();
        }

        private void Tabs_Selected(object sender, TabControlEventArgs e)
        {
            if (e.TabPage == ConfigurationTab)
            {
                LoadConfigTabValues();
            }
            else if (e.TabPage == PerfCounterTab)
            {
                LoadPerfCounterValues();
            }
            else if (e.TabPage == WindowsLogTab)
            {
                LoadWindowsEventLogValues();
            }
            else if (e.TabPage == AzureLogTab)
            {
                LoadAzureLoadValues();
            }
        }

        private void StatusUpdateTimer_Tick(object sender, EventArgs e)
        {
            DownloadStatus.Text = _azureDataDownload.DownloadStatus;
        }

        private void Monitor_Resize(object sender, EventArgs e)
        {
            if (Tabs.SelectedTab == PerfCounterTab)
            {
                LoadPerfCounterValues();
            }
        }

        private void TabUpdateTimer_Tick(object sender, EventArgs e)
        {
            if (_azureDataDownload.UpdatingPerformanceCounters || _lastUpdateToken != _azureDataDownload.UpdateToken)
            {
                if (Tabs.SelectedTab == PerfCounterTab)
                {
                    LoadPerfCounterValues();
                }
            }

            if (_lastUpdateToken != _azureDataDownload.UpdateToken)
            {
                if (Tabs.SelectedTab == WindowsLogTab)
                {
                    LoadWindowsEventLogValues();
                }
            }

            _lastUpdateToken = _azureDataDownload.UpdateToken;
        }

        #region ConfigurationTab

        private void LoadConfigTabValues()
        {
            DatabaseServerName.Text = _settings.DatabaseServer;
            DatabaseName.Text = _settings.DatabaseName;
            AzureAccountName.Text = _settings.AzureAccountName;
            AzureBlobUrl.Text = _settings.AzureBlobUrl;
            AzureDownloadInterval.Value = _settings.AzureDownloadInterval;
            AzureQueueUrl.Text = _settings.AzureQueueUrl;
            AzureSharedKey.Text = _settings.AzureSharedApiKey;
            AzureTableUrl.Text = _settings.AzureTableUrl;
        }

        private void CancelConfigChanges_Click(object sender, EventArgs e)
        {
            LoadConfigTabValues();
        }

        private void ApplyConfigChanges_Click(object sender, EventArgs e)
        {
            _settings.DatabaseServer = DatabaseServerName.Text;
            _settings.DatabaseName = DatabaseName.Text;
            _settings.AzureAccountName = AzureAccountName.Text;
            _settings.AzureBlobUrl = AzureBlobUrl.Text;
            _settings.AzureDownloadInterval = (int)AzureDownloadInterval.Value;
            _settings.AzureQueueUrl = AzureQueueUrl.Text;
            _settings.AzureSharedApiKey = AzureSharedKey.Text;
            _settings.AzureTableUrl = AzureTableUrl.Text;

            _settings.Save();

            _storage = new SqlStorage(_settings);
            _azureDataDownload.UpdateStorage();
        }
        #endregion

        #region PerfCounterTab

        private void LoadPerfCounterValues()
        {
            if (_storage != null)
            {
                _oldestDate = _storage.OldestCounter(SelectedCounters(), SelectedRoleInstances());
                _newestDate = _storage.NewestCounter(SelectedCounters(), SelectedRoleInstances());

                if (_oldestDate == null || _newestDate == null)
                {
                    GraphPosition.Minimum = 0;

                    PerformanceCounterGraph.Counters = new List<MonitoredPerformanceCounter>();
                    PerformanceCounterGraph.CounterValues = new List<CounterValue>();
                }
                else
                {
                    int minScroll = -((int)_newestDate.Value.Subtract(_oldestDate.Value).TotalMinutes - Width);
                    if (minScroll > 0)
                    {
                        minScroll = 0;
                    }

                    GraphPosition.Minimum = minScroll;
                    GraphPosition.LargeChange = PerformanceCounterGraph.Width / 10;
                    GraphPosition.Maximum = GraphPosition.LargeChange;

                    PerformanceCounterGraph.Counters = SelectedPerformanceCounters();
                }

                RenderCounters();
            }
        }

        private void RenderCounters()
        {
            if (_newestDate != null)
            {
                int graphPoistionValue = GraphPosition.Value;

                if (graphPoistionValue > 0)
                {
                    graphPoistionValue = 0;
                }

                DateTime renderEndDate = _newestDate.Value.AddMinutes(graphPoistionValue);
                DateTime renderStartDate = renderEndDate.AddMinutes(-PerformanceCounterGraph.Width);

                PerformanceCounterGraph.NewestDate = renderEndDate;
                PerformanceCounterGraph.CounterValues = _storage.GetCounterValues(renderStartDate, renderEndDate, SelectedCounters(), SelectedRoleInstances());
            }

            PerformanceCounterGraph.Refresh();
        }

        private IEnumerable<MonitoredPerformanceCounter> SelectedPerformanceCounters()
        {
            foreach (ListViewItem item in CurrentCounters.Items)
            {
                yield return (MonitoredPerformanceCounter)item.Tag;
            }
        }

        private IEnumerable<Counter> SelectedCounters()
        {
            if (_storage == null)
            {
                return new List<Counter>();
            }

            if (_currentCounters == null)
            {
                _currentCounters = _storage.Counters.ToList();
            }

            return from pc in SelectedPerformanceCounters()
                   join c in _currentCounters on pc.CounterId equals c.CounterId
                   select c;
        }

        private IEnumerable<RoleInstance> SelectedRoleInstances()
        {
            if (_storage == null)
            {
                return new List<RoleInstance>();
            }

            if (_currentRoleInstances == null)
            {
                _currentRoleInstances = _storage.RoleInstances.ToList();
            }

            return from pc in SelectedPerformanceCounters()
                   join ri in _currentRoleInstances on pc.RoleInstanceId equals ri.RoleInstanceId
                   select ri;
        }

        private void AddCounterButton_Click(object sender, EventArgs e)
        {
            AddCounter dlg = new AddCounter(_storage);

            ClearDbCaches();

            if (dlg.ShowDialog(this) == DialogResult.OK)
            {
                MonitoredPerformanceCounter perfCounter = new MonitoredPerformanceCounter()
                {
                    RoleInstanceId = dlg.SelectedRoleInstance.RoleInstanceId,
                    RoleInstanceName = dlg.SelectedRoleInstance.RoleInstanceName,
                    CounterId = dlg.SelectedCounter.CounterId,
                    CounterName = dlg.SelectedCounter.CounterName,
                    Scale = 1,
                    StyleColor = Color.Red
                };

                ListViewItem item = new ListViewItem();
                item.Tag = perfCounter;

                UpdateListItem(item);

                CurrentCounters.Items.Add(item);

                SaveCounters();

                LoadPerfCounterValues();
            }
        }

        private void ClearDbCaches()
        {
            _currentCounters = null;
        }

        private void SaveCounters()
        {
            BinaryFormatter formatter = new BinaryFormatter();

            using (MemoryStream ms = new MemoryStream())
            {
                formatter.Serialize(ms, SelectedPerformanceCounters().Count());

                foreach (MonitoredPerformanceCounter counter in SelectedPerformanceCounters())
                {
                    formatter.Serialize(ms, counter);
                }

                _settings.SelectedCounters = Convert.ToBase64String(ms.ToArray());
            }

            _settings.Save();
        }

        private void UpdateListItem(ListViewItem item)
        {
            MonitoredPerformanceCounter perfCounter = (MonitoredPerformanceCounter)item.Tag;

            item.SubItems.Clear();

            item.Text = perfCounter.StyleColor.Name;
            item.SubItems.Add(perfCounter.RoleInstanceName);
            item.SubItems.Add(perfCounter.CounterName);
            item.SubItems.Add(string.Format("{0:0.########}", perfCounter.Scale));
        }

        private void GraphPosition_Scroll(object sender, ScrollEventArgs e)
        {
            RenderCounters();
        }

        private void RemoveCounterButton_Click(object sender, EventArgs e)
        {
            if (CurrentCounters.SelectedItems.Count != 0)
            {
                foreach (ListViewItem item in new List<ListViewItem>(CurrentCounters.SelectedItems.OfType<ListViewItem>()))
                {
                    CurrentCounters.Items.Remove(item);
                }
            }

            PerformanceCounterGraph.Refresh();
            SaveCounters();
        }

        private void CounterPropertiesButton_Click(object sender, EventArgs e)
        {
            if (CurrentCounters.SelectedItems.Count != 0)
            {
                CounterProperties dlg = new CounterProperties((MonitoredPerformanceCounter)CurrentCounters.SelectedItems[0].Tag, _storage);

                if (dlg.ShowDialog(this) == DialogResult.OK)
                {
                    UpdateListItem(CurrentCounters.SelectedItems[0]);

                    SaveCounters();

                    PerformanceCounterGraph.Refresh();
                }
            }
        }

        private void CurrentCounters_DoubleClick(object sender, EventArgs e)
        {
            CounterPropertiesButton_Click(sender, e);
        }

        private void splitContainer1_SplitterMoved(object sender, SplitterEventArgs e)
        {
            RenderCounters();
        }

        private void LoadMonitoredPerfCounters()
        {
            BinaryFormatter formatter = new BinaryFormatter();

            CurrentCounters.Items.Clear();

            if (!string.IsNullOrEmpty(_settings.SelectedCounters))
            {
                try
                {
                    using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(_settings.SelectedCounters)))
                    {
                        int totalCounters = (int)formatter.Deserialize(ms);

                        for (int count = 0; count != totalCounters; count++)
                        {
                            MonitoredPerformanceCounter counter = (MonitoredPerformanceCounter)formatter.Deserialize(ms);

                            ListViewItem item = new ListViewItem();
                            item.Tag = counter;

                            UpdateListItem(item);

                            CurrentCounters.Items.Add(item);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logging.LogException("LoadMonitoredPerfCounters", ex);
                }
            }
        }

        internal MonitoredPerformanceCounter SelectedPerformanceCounter
        {
            get
            {
                if (CurrentCounters.SelectedItems.Count == 0)
                {
                    return null;
                }

                return (MonitoredPerformanceCounter)CurrentCounters.SelectedItems[0].Tag;
            }
        }

        private void CurrentCounters_SelectedIndexChanged(object sender, EventArgs e)
        {
            PerformanceCounterGraph.Refresh();
        }
        #endregion

        #region WindowsEventLogsTab
        private void LoadWindowsEventLogValues()
        {
            WELDeployment.DataSource = _storage.Deployments;
            WELChannel.DataSource = _storage.Channels;

            UpdateWindowsLogEventList();
        }

        private void WELDeployment_SelectedIndexChanged(object sender, EventArgs e)
        {
            WELRole.DataSource = (from r in _storage.Roles
                                  where r.DeploymentId == ((Deployment)WELDeployment.SelectedItem).DeploymentId
                                  select r).ToList();
        }

        private void WELRole_SelectedIndexChanged(object sender, EventArgs e)
        {
            WELRoleInstance.DataSource = (from ri in _storage.RoleInstances
                                          where ri.RoleId == ((Role)WELRole.SelectedItem).RoleId
                                          select ri).ToList();
        }

        private void WELRoleInstance_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateWindowsLogEventList();
        }

        private void WELChannel_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateWindowsLogEventList();
        }

        private void UpdateWindowsLogEventList()
        {
            WELEvents.Items.Clear();
            WELDelete.Enabled = false;
            WELEventDetails.Rows.Clear();

            foreach (WindowsLogEntry windowsLogEntry in _storage.GetWindowsEventLogEntries((RoleInstance)WELRoleInstance.SelectedItem, (Channel)WELChannel.SelectedItem))
            {
                ListViewItem lvi = new ListViewItem();

                lvi.Text = windowsLogEntry.EventTime.ToLocalTime().ToString("MM/dd/yyyy hh:mm:ss.tttt");
                lvi.SubItems.Add(windowsLogEntry.EventId.ToString());
                lvi.SubItems.Add(windowsLogEntry.Level.ToString());
                lvi.SubItems.Add(windowsLogEntry.ProviderName);
                lvi.Tag = windowsLogEntry;

                WELEvents.Items.Add(lvi);
            }
        }

        private void WELEvents_SelectedIndexChanged(object sender, EventArgs e)
        {
            WELEventDetails.Rows.Clear();

            if (WELEvents.SelectedItems.Count > 0)
            {
                WELDelete.Enabled = true;

                XDocument logEntry = XDocument.Parse(((WindowsLogEntry)WELEvents.SelectedItems[0].Tag).LogXml);

                XNamespace ns = "http://schemas.microsoft.com/win/2004/08/events/event";
                foreach (XElement detail in logEntry.Element(ns + "Event").Element(ns + "System").Elements())
                {
                    if (!string.IsNullOrEmpty(detail.Value))
                    {
                        WELEventDetails.Rows.Add();

                        DataGridViewRow row = WELEventDetails.Rows[WELEventDetails.Rows.Count - 1];

                        row.Cells[0].Value = detail.Name.LocalName;
                        row.Cells[1].Value = detail.Value;
                    }

                    foreach (XAttribute attr in detail.Attributes())
                    {
                        WELEventDetails.Rows.Add();

                        DataGridViewRow row = WELEventDetails.Rows[WELEventDetails.Rows.Count - 1];

                        row.Cells[0].Value = detail.Name.LocalName + "." + attr.Name.LocalName;
                        row.Cells[1].Value = attr.Value;
                    }
                }

                if (logEntry.Element(ns + "Event").Element(ns + "EventData") != null)
                {
                    int firstDataRow = WELEventDetails.Rows.Count + 1;

                    foreach (XElement data in logEntry.Element(ns + "Event").Element(ns + "EventData").Elements(ns + "Data"))
                    {
                        foreach (string line in data.Value.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            WELEventDetails.Rows.Add();

                            DataGridViewRow row = WELEventDetails.Rows[WELEventDetails.Rows.Count - 1];

                            row.Cells[1].Value = line.Trim();
                        }
                    }

                    if (firstDataRow <= WELEventDetails.Rows.Count)
                    {
                        WELEventDetails.Rows[firstDataRow - 1].Cells[0].Value = "Data:";
                    }
                }
            }
            else
            {
                WELDelete.Enabled = false;
            }
        }

        private void WELDelete_Click(object sender, EventArgs e)
        {
            _storage.DeleteEntry((WindowsLogEntry)WELEvents.SelectedItems[0].Tag);

            UpdateWindowsLogEventList();
        }
        #endregion

        #region AzureEventLogs
        private void LoadAzureLoadValues()
        {
            ALDeployment.DataSource = _storage.Deployments;
            UpdateAzureEntryList();
        }

        private void UpdateAzureEntryList()
        {
            ALEntries.Items.Clear();
            ALDelete.Enabled = false;
            ALEntryDetails.Text = "";

            foreach (AzureLogEntry azureLogEntry in _storage.GetAzureLogEntries((RoleInstance)ALRoleInstance.SelectedItem))
            {
                ListViewItem lvi = new ListViewItem();

                lvi.Text = azureLogEntry.EventTime.ToLocalTime().ToString("MM/dd/yyyy hh:mm:ss.tttt");
                lvi.SubItems.Add(azureLogEntry.EventId.ToString());
                lvi.SubItems.Add(azureLogEntry.Level.ToString());
                lvi.SubItems.Add(azureLogEntry.Pid.ToString());
                lvi.SubItems.Add(azureLogEntry.Tid.ToString());
                lvi.Tag = azureLogEntry;

                ALEntries.Items.Add(lvi);
            }
        }

        private void ALDeployment_SelectedIndexChanged(object sender, EventArgs e)
        {
            ALRole.DataSource = (from r in _storage.Roles
                                 where r.DeploymentId == ((Deployment)ALDeployment.SelectedItem).DeploymentId
                                 select r).ToList();
        }

        private void ALRole_SelectedIndexChanged(object sender, EventArgs e)
        {
            ALRoleInstance.DataSource = (from ri in _storage.RoleInstances
                                         where ri.RoleId == ((Role)ALRole.SelectedItem).RoleId
                                         select ri).ToList();
        }

        private void ALRoleInstance_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateAzureEntryList();
        }

        private void ALEntries_SelectedIndexChanged(object sender, EventArgs e)
        {
            ALEntryDetails.Text = "";

            if (ALEntries.SelectedItems.Count > 0)
            {
                ALDelete.Enabled = true;

                string message = ((AzureLogEntry)ALEntries.SelectedItems[0].Tag).Message;

                ALEntryDetails.Text = message.Replace("\n", "\r\n");
            }
            else
            {
                ALDelete.Enabled = false;
            }
        }

        private void ALDelete_Click(object sender, EventArgs e)
        {
            _storage.DeleteEntry((AzureLogEntry)ALEntries.SelectedItems[0].Tag);

            UpdateAzureEntryList();
        }
        #endregion
    }
}
